package bankingSystemClient;

import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Vector;

import javax.swing.*;

import bankingSystemClient.ClientGUIView.ClientModel;
import bankingSystemServer.AccountInfo;

import communicationClasses.LoginRequest;
import communicationClasses.TransactionReply;

/**
 * Used to login to the banking system. Creates a connection to the server and
 * validates the users credentials by passing a LoginRequest and receives a
 * LoginReply.
 * 
 * @author Jason Brown
 * 
 */
public class LoginBox extends JFrame {
	
	public static String SERVER_ADDRESS = "localhost";
	public static int SERVER_PORT = 49999;

	/**
	 * This class is classless
	 */
	private static final long serialVersionUID = -6334972953207744260L;
	public static int LISTENING_PORT = 49999;
	JPanel loginPanel;
	GridLayout gl;
	JLabel accountLabel, passwordLabel;
	JTextField accountjTextField;
	JPasswordField passwordField;
	JButton submitButton;
	ClientModel cm;
	Socket socket;
	ObjectOutputStream objectOutputStream;
	ObjectInputStream objectInputStream;
	TransactionReply transactionReply;
	AccountInfo accountInfo;
	Vector<AccountInfo> accounts;

	/**
	 * Creates a new LoginBox and calls initComponents() to initializes the
	 * components
	 * 
	 * @param cm
	 * @param socket
	 * @param oos
	 * @param ois
	 */
	public LoginBox(ClientModel cm, Socket socket, ObjectOutputStream oos,
			ObjectInputStream ois, Vector<AccountInfo> a) {
		this.cm = cm;
		this.socket = socket;
		this.objectOutputStream = oos;
		this.objectInputStream = ois;
		this.accounts = a;
		initComponents();
		this.setLocationRelativeTo(null);
	}

	/**
	 * Closes the loginbox after successful login
	 */
	public void closeLoginBox() {
		this.setVisible(false);
		this.dispose();
		System.out.println("disposed login box");
	}

	/**
	 * Initializes the components
	 */
	private void initComponents() {
		loginPanel = new JPanel();
		gl = new GridLayout(3, 2, 1, 1);
		accountLabel = new JLabel("Account#");
		passwordLabel = new JLabel("Password");
		accountjTextField = new JTextField();
		passwordField = new JPasswordField();
		submitButton = new JButton("LOGIN");
		submitButton.addActionListener(new SubmitListener());
		loginPanel.setLayout(gl);
		loginPanel.add(accountLabel);
		loginPanel.add(accountjTextField);
		loginPanel.add(passwordLabel);
		loginPanel.add(passwordField);
		loginPanel.add(new JLabel());
		loginPanel.add(submitButton);
		this.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
		this.setSize(400, 140);
		this.add(loginPanel);
		this.setAlwaysOnTop(isAlwaysOnTopSupported());
		this.setTitle("Logon Box");
		this.setResizable(false);

	}

	/**
	 * Private class to handle events for the submit button
	 * 
	 * @author Jay B
	 * 
	 */
	private class SubmitListener implements ActionListener {

		public void actionPerformed(ActionEvent e) {
			String accountNumber = accountjTextField.getText();
			String password = getPasswordText(passwordField.getPassword());

			// Confirm account number conforms to account number invariance
			if (checkAccountNumberLength(accountNumber)) {
				System.out.println("Account number check passed\n");
				System.out.println("This is the password " + password + "\n");

				// attempt to login to bank system
				if (attemptLogin(accountNumber, password)) {
					cm.writeToTextArea("You are logged in\n\nRetrieving account information");
					
					accounts = transactionReply.getAccounts();
					cm.setAccounts();
					cm.updateGUIFields();
					cm.enableControls();
					closeLoginBox();
				}

			} else {
				JOptionPane.showMessageDialog(null,
						"Invalid password. Try again.", "Login Failed",
						JOptionPane.ERROR_MESSAGE);
			}
		}

		/**
		 * Verifies the account number has a valid length, if the length is not
		 * valid, a login attempt is not attempted
		 * 
		 * @param account
		 * @return
		 */
		private boolean checkAccountNumberLength(String account) {
			if (account.length() == 4)
				return true;
			JOptionPane.showMessageDialog(null, "Invalid Account Number.",
					"Login Failed", JOptionPane.ERROR_MESSAGE);
			return false;
		}

		/**
		 * Converts the password to a string
		 * 
		 * @param password
		 * @return
		 */
		private String getPasswordText(char[] password) {
			return String.valueOf(password, 0, password.length);
		}

		/**
		 * Creates a socket and attempts to connect to the server.
		 * 
		 * @param accountNumber
		 * @param password
		 * @return
		 */
		private boolean attemptLogin(String accountNumber, String password) {
			try {
				LoginRequest loginRequest = new LoginRequest(accountNumber,
						password);

				// create the socket connection to the server
				System.out
						.println("creating socket, connection to localhost on port 49999");
				socket = new Socket(SERVER_ADDRESS, SERVER_PORT);
				System.out.println("socket created!");

				// send the login request
				OutputStream outputStream = socket.getOutputStream();
				ObjectOutputStream objectOutputStream = new ObjectOutputStream(
						outputStream);
				objectOutputStream.writeObject(loginRequest);

				// get the login reply from the server
				InputStream inputStream = socket.getInputStream();
				ObjectInputStream objectInputStream = new ObjectInputStream(
						inputStream);
				Object messageObject = objectInputStream.readObject();
				if (messageObject instanceof TransactionReply) {
					transactionReply = (TransactionReply) messageObject;					
					System.out.println("Got transaction reply\n");
					if (transactionReply.getResult()) {
						return true;
					} else {
						objectInputStream.close();
						inputStream.close();
						socket.close();
						return false;
					}
				}

			} catch (Exception e) {
				e.printStackTrace();
			}
			return false;
		}

	}

	public Socket getSocket() {
		return socket;
	}

	public AccountInfo getAccountInfo() {
		return accountInfo;
	}
	
	public Vector<AccountInfo> getAccounts() {
		return accounts;
	}

}
